Release 10.1A: OpenEdge Development:
ProDataSets
Elements of a Business Entity
In principle, a Business Entity as an object can map to a Progress 4GL procedure. This might in turn be supported by any number of other procedures that provide either specific validation logic for the entity or general support services used by all entities. Each Business Entity is normally paired with a Data Access object that manages the connection to the actual data sources. As discussed earlier, in many cases a single running instance of a Data Access object could provide data for any number of requests from different Business Entity instances.
In a distributed stateless environment, a server-side Business Entity will normally not live beyond a single request. It can fill its ProDataSet for a particular
Order, for example, return thatOrderto the client, and then either terminate or remain in memory to be used by another independent request, depending on how the application manages its procedures. On the other hand, if one Business Entity is used by another Business Entity in the same session, as part of its validation logic, for example, then there could be a relationship between the two procedures that would last beyond a single request.As with the Data Access object, the Business Entity will typically be a static object. That is, it will be based on one or more static ProDataSet definitions, and contain (directly or indirectly) mostly specific logic to support its use. This logic will include a specific API for the various kinds of requests that can be made of it, and validation logic to apply to update requests. A dynamic Business Entity designed to handle a certain class of similar data objects is also entirely possible, of course.
Given these basics, the following subsections outline some of the common elements of the Business Entity.
ProDataSet and temp-table definitions
A Business Entity will typically contain the same temp-table and ProDataSet definitions as its Data Access object. The significant different is that it is the Business Entity’s instance of the ProDataSet that is actually filled with data used to satisfy a request, when it is passed
BY-REFERENCEto its Data Access object.Relationship to the Data Access object
If the application model is that an instance of the Data Access object serves a single Business Entity instance, then the Business Entity can run that instance as a persistent procedure, as in this example:
The Entity can then use the API of the Data Access object to run support methods in this procedure handle.
The Business Entity could also make the Data Access object a super procedure of the Business Entity, as shown:
This allows the Entity to use the API of the Data Access object as if it were part of its own procedure.
Using the super procedure technique also changes the nature of calls from outside that are routed from the Business Entity to the Data Access object. This is discussed in the "Data retrieval API" section.
Attaching the Data-Sources and callbacks
The entity procedure can, as part of its main block startup code, request that the Data Access object attach Data-Sources and set callback procedures to its ProDataSet instance, as in this example:
As was illustrated earlier, this can also be done in each of the methods of the Data Access object, rather than as a separate step.
Defining a data retrieval API
Any requests for data should be made to the Business Entity, never directly from outside to the Data Access object. This simply preserves the isolation of the different layers of the application. The
fetchOrderprocedure inOrderEntity.pis an example of this:
This turns around and runs an equivalent procedure in the Data Access procedure. Significantly, the ProDataSet parameter is
INPUT-OUTPUT BY-REFERENCEin the second-level call to the Data Access object. This uses the Business Entity’s ProDataSet and avoids the expense of copying the ProDataSet back and forth. Having avoided this, the overhead of the second procedure call is not very significant.Figure 11–2 shows how the ProDataSet is being used in this case.
Figure 11–2: ProDataSet flow
![]()
Here is an outline of the steps illustrated in Figure 11–2:
- A requesting procedure on the client runs
fetchOrderin theOrderBusiness Entity on the server.- That
fetchOrderprocedure runsfetchOrderin the Data Access object in its session.- Because the ProDataSet is passed in
BY-REFERENCE, it is actually the Business Entity’s instance that is used (marked in bold). The dotted lines indicate that this instance is passed without being copied.- The
fetchOrderprocedure in the Data Access object attaches Data-Sources, which are tables in the database, and also sets any callback procedures forFILLevents.- It then does a
FILL, which actually fills the ProDataSet instance back in the Business Entity.- It returns the ProDataSet to the requesting procedure. It is copied there rather than being passed
BY-REFERENCEbecause the presumption is that the requester is or may be in a different session.Alternatively, if the Data Access object is a super procedure of the Business Entity, then in cases where the Business Entity version of a procedure like
fetchOrderdoesn’t do any additional work of its own, it could be dispensed with, and a call tofetchOrderfrom another procedure would be handled automatically by the Data Access object. In this case, there are a couple of things to consider:
- First, the Data Access object version of
fetchOrderwould need to make its ProDataSet parameterOUTPUTinstead ofINPUT-OUTPUT, because it would be passed back directly to the caller. The caller would not be passing in a ProDataSet of its own. In this case, the Data Access object’s ProDataSet instance is the one that is used to satisfy the request. It then becomes the Data Access object’s responsibility to make sure the Data-Sources are attached, as illustrated in the second version offetchOrderin the "Data Access object template" section.- Second, if there is any reason for the Business Entity procedure to run
fetchOrderin the Data Access procedure, or to provide extended behavior for a call from outside tofetchOrder, then this arrangement becomes inefficient, because the ProDataSet would be copied from the Data Access procedure to the Business Entity procedure. For example, consider this alternative tofetchOrderin the Entity:
If the parameter definition for the ProDataSet in
fetchOrderin the procedureOrderSource.pis changed to beOUTPUTinstead ofINPUT-OUTPUT, andhSourceProcis a super procedure ofOrderEntity.p, andfetchOrderinOrderSource.pdoes theattachDataSet, then this arrangement works fine.However, the ProDataSet is copied from
OrderSource toOrderEntity before being copied back to the caller. This is not a good thing. Because of this, and because of the potential for confusion between when the Data Access ProDataSet instance is being used and when the Business Entity instance is being used, making the data access procedure a super procedure may not be a good practice.Figure 11–3 illustrates what happens if the Data Access object is a super procedure of the Business Entity, and it has the only implementation of procedure
fetchOrder.Figure 11–3: Data Access object as super procedure
![]()
Here are the steps illustrated in Figure 11–3:
- The requesting procedure runs
fetchOrderin the Business Entity as before.- There is no
fetchOrderprocedure in the Business Entity. However, since the Data Access object is a super procedure, Progress runsfetchOrderthere.- This means that it is the Data Access object’s ProDataSet instance that is used for the request.
- The
fetchOrderprocedure attaches Data-Sources and callback procedures to this ProDataSet.- It then fills the ProDataSet.
- The
fetchOrderprocedure then returns this ProDataSet to the original caller as anOUTPUTparameter. The ProDataSet instance in the Business Entity is not used.This all works correctly in this case, but could be a source of confusion and errors because the ProDataSet instance being used is not consistent.
By contrast, Figure 11–4 shows the case where, once again, the Data Access object is a super procedure of the Business Entity, and
fetchOrderin the Business Entity does aRUN SUPERto run the standard attach and fill behavior.Figure 11–4: Data Access object as super procedure with RUN SUPER
![]()
Here are the steps illustrated in Figure 11–4:
- The requesting procedure runs
fetchOrderin the Business Entity as before. There is an implementation offetchOrderthere, so it is executed.- Procedure
fetchOrderdoes aRUN SUPER, which runsfetchOrderin the Data Access object. Because the parameter definitions must be consistent in this case, the ProDataSet is simply anOUTPUTparameter.- Because of this, the Data Access object uses its own ProDataSet instance.
- It attaches Data-Sources to its own ProDataSet instance.
- It fills its own ProDataSet instance.
- It returns its ProDataSet as an
OUTPUTparameter to the Business Entity, copying it to the Business Entity’s ProDataSet instance.- The Business Entity then returns it to the original caller as
OUTPUT, again copying the ProDataSet.Because of the extra copy operation, this is not a good configuration. This is something you need to keep in mind when you design your procedures and decide how they are related.
This discussion may seem complex, but the intention is to make you aware of some of the issues and how you should consider them when you’re designing your application. As with every other aspect of design, once you have thought through an appropriate solution to a part of your design, if you keep to your solution consistently, then you won’t have to worry about it anymore, and developers writing business logic don’t need to be concerned about the details of the Data Access architecture supporting them.
Defining a generic update API
As the sample procedures in earlier chapters show, you can create a general-purpose update API that can take changes made to any ProDataSet and apply them to the database, even executing validation procedures for the specific ProDataSet if they conform to a standard naming convention. This approach is very much like how the SmartDataObject and SmartBusinessObject in the ADM2 handle their update logic.
The generic update procedure in the samples is
commitChanges.p. The sample entity procedure,OrderEntity.p, wraps this in a call of its own to provide an API for other procedures to use, as shown:
This wrapper procedure attaches the Data-Sources and event handlers, runs
commitChanges.p, and then detaches the Data-Sources. This could as easily be incorporated directly into a procedure likecommitChangesif it knows where to run the attach and detach methods. Beyond that, having a wrapper procedure gives the specific object an opportunity to add special commit logic before or after the standard code.Validation procedures for the generic update API
For example, the
OrderEntityprocedure has this sample validation procedure which based on its name will be executed whenever a row in thettOlinetable in the ProDataSet is modified:
The basic principles of writing procedures such as this one are:
- You can pass in the entire ProDataSet
BY-REFERENCEfrom another procedure in the same session without the cost of copying it, so that the validation logic can examine any part of the ProDataSet that it needs to.- The current rows in the buffers will be those that were current in the caller. In particular, the current row in the table that triggered the event is the one that was modified, so you can examine its values without having to
FINDit.Validation procedures can set the
ERRORand/orERROR-STRINGattributes for the row to communicate status information. SettingERROR-STRINGwithout setting error lets you return an informational or warning message without causing an actual error.Defining a custom update API
In addition to using a standard update mechanism for general updates, there can of course be additional more specific update API calls in your Business Entity that handle particular situations where the default behavior is not sufficient.
Managing Business Entity instances
Managing Business Entities and their Data Access objects needs to be coordinated in some fashion, so that entities can locate each other, can be accessed from client requests, and can be started and stopped when appropriate.
In general, Business Entities and their APIs should be designed such that there are no dependencies between requests. This is especially necessary for a stateless distributed environment where a client cannot expect to be given access to the same procedure instance on an AppServer in successive calls. Thus, there is little reason to leave data in a Business Entity on the AppServer after a request completes.
This means that a Business Entity instance can be left running on an AppServer to service any number of unrelated requests. If the Business Entity procedures themselves are not enormous amount of r-code, it may be just as efficient to destroy each instance after its use and start a fresh instance when needed. It takes very little time to load a compiled procedure into memory. Any significant overhead is in startup code for the object, and this should be minimized. Keep in mind that while only one business instance is needed in an AppServer session to satisfy any number of successive requests from client sessions, the business logic within a session may need to use (and possibly start) other Business Entities to execute its own logic.
There should be a mapping between Business Entity names as used in business logic and actual procedure names, so that objects do not need to be aware of other procedure names to run them directly. This can be repository-based, and managed by a single session management utility within the session that accepts requests for entities and either locates or starts them and returns their handles. For example, an
Orderentity should be able to make a request of theCustomerentity without having to know an actual procedure name to run. It should be the responsibility of the entity manager to handle this.Support procedures that require only a single instance within a session, such as Data Access procedures, and most supporting business logic and update validation procedures, cam be started by the Business Entities or started by the entity management utility the first time they are needed, and then left running for the duration of the session, or else shut down on some form of LRU basis if memory becomes a problem.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |